package mission

import (
	"encoding/json"
	"sanguo/node/node_game"
	missionDef "sanguo/node/table/mission"
	cs_message "sanguo/protocol/cs/message"

	"github.com/golang/protobuf/proto"
)

type MissionEventReceiver interface {
	OnMissionAccept(*Mission) //任务接取调用，剧情阶段
	OnMissionEnd(*Mission)    //任务完结调用，可在里面发放奖励
	OnMissionDirty()          //任意任务状态/条件发生改变后调用，可在里面将dirty的任务列表同步给玩家
}

//任务条件
type MissionCond struct {
	TT    int32 //类型
	Id    int32 //id
	Count int32
	Need  int32
}

const (
	mission_unacceptable = 1 //不可接受
	mission_acceptable   = 2 //待接受
	mission_running      = 3 //正在执行
	mission_finishable   = 4 //待交付
	mission_end          = 5 //任务完结

)

type Mission struct {
	Id              int32 //任务ID
	TT              int32 //任务类型
	State           int32
	Count           int32 //任务接取次数
	Cond            map[int32]*MissionCond
	NextRefreshTime int64
}

func FuncNewMission(missionID int32) *Mission {
	m := newMission(missionID)
	if m.AutoAccept() {
		m.State = mission_running
	}
	return m
}

func newMission(missionID int32) *Mission {
	def := missionDef.GetDef(missionID)
	if nil != def {
		mission := &Mission{
			Id:              def.BaseInfo.Id,
			TT:              def.BaseInfo.Type,
			State:           mission_acceptable,
			Count:           1,
			Cond:            map[int32]*MissionCond{},
			NextRefreshTime: 0,
		}
		for _, v := range def.CompleteInfo.ConditionList.Conditions {
			missionCond := &MissionCond{
				Id:    v.Id,
				TT:    v.Type,
				Need:  v.Need,
				Count: 0,
			}
			mission.Cond[v.Id] = missionCond
		}

		return mission

	}

	return nil
}

func (this *Mission) GetMissionId() int32 {
	return this.Id
}

func (this *Mission) GetMissionState() int32 {
	return this.State
}

func (this *Mission) GetMissionCond(condID int32) *MissionCond {
	return this.Cond[condID]
}

func (this *Mission) AutoAccept() bool {
	def := missionDef.GetDef(this.Id)
	if nil != def {
		return def.BaseInfo.AutoAccept
	} else {
		return false
	}
}

func (this *Mission) AutoComplete() bool {
	def := missionDef.GetDef(this.Id)
	if nil != def {
		return def.BaseInfo.AutoComplete
	} else {
		return false
	}
}

func (this *Mission) DayLimit() int32 {
	def := missionDef.GetDef(this.Id)
	if nil != def {
		return def.BaseInfo.DayLimit
	} else {
		return 0
	}
}

func (this *Mission) NextMissionID() int32 {
	def := missionDef.GetDef(this.Id)
	if nil != def {
		return def.BaseInfo.NextMissionID
	} else {
		return 0
	}
}

func (this *Mission) GetStatePB() *cs_message.MissionState {
	if this.State == mission_unacceptable {
		return cs_message.MissionState(cs_message.MissionState_UnAcceptAble).Enum()

	} else if this.State == mission_acceptable {
		return cs_message.MissionState(cs_message.MissionState_AcceptAble).Enum()

	} else if this.State == mission_running {
		return cs_message.MissionState(cs_message.MissionState_Running).Enum()

	} else if this.State == mission_finishable {
		return cs_message.MissionState(cs_message.MissionState_FinishAble).Enum()

	} else {
		return cs_message.MissionState(cs_message.MissionState_End).Enum()

	}

}

func (this *Mission) GetCondPB() []*cs_message.MissionCond {
	ret := []*cs_message.MissionCond{}
	for _, v := range this.Cond {
		c := &cs_message.MissionCond{
			Tt:    proto.Int32(v.TT),
			Id:    proto.Int32(v.Id),
			Count: proto.Int32(v.Count),
		}
		ret = append(ret, c)
	}
	return ret

}

func (this *Mission) IsConditionOK() bool {
	for _, v := range this.Cond {
		if v.Count < v.Need {
			return false
		}
	}
	return true
}

func (this *Mission) AddCondition(tt, id, count int32) {

	for _, v := range this.Cond {
		if v.TT == tt && v.Id == id /*&& v.Count < v.Need */ {
			v.Count = v.Count + count
			if v.Count >= v.Need {
				v.Count = v.Need
			}
		}
	}
}

type RoleMission struct {
	missions     map[int32]*Mission
	missionDirty map[int32]bool //发生了变更尚未同步给客户端的任务
	dbDirty      bool           //是否需要刷新到数据库
	receiver     MissionEventReceiver
}

func (this *RoleMission) GetMissions() map[int32]*Mission {
	return this.missions
}

func (this *RoleMission) Init(receiver MissionEventReceiver, missions map[int32]*Mission) {
	this.missions = missions
	this.missionDirty = map[int32]bool{}
	this.receiver = receiver

}

//获取running中的任务
func (this *RoleMission) GetRunningMission() []*Mission {
	missions := []*Mission{}
	for _, v := range this.missions {
		if v.State == mission_running {
			missions = append(missions, v)
		}
	}
	return missions
}

//根据任务条件ID,类型Type 获取任务
func (this *RoleMission) GetMissionByCond(condID int32, condType int32) *Mission {
	missions := this.GetRunningMission()
	for _, v := range missions {
		if v.Cond[condID] != nil && v.Cond[condID].TT == condType {
			return v
		}
	}
	return nil
}

//获取初始化后unacceptable的任务
func (this *RoleMission) GetUnAcceptMission() []*Mission {
	missions := []*Mission{}
	for _, v := range this.missions {
		if v.State == mission_unacceptable {
			missions = append(missions, v)
		}
	}
	return missions
}

func (this *RoleMission) GetMission(missionID int32) *Mission {
	return this.missions[missionID]
}

func (this *RoleMission) AddMissionCond(missionID, condType, condID, count int32) {
	m := this.missions[missionID]
	if nil != m {
		m.AddCondition(condType, condID, count)
		if m.IsConditionOK() {
			if m.AutoComplete() {
				m.State = mission_end

				next := m.NextMissionID()

				if next > 0 {
					this.AddNextMission(next)
				}
				this.receiver.OnMissionEnd(m)
			} else {
				m.State = mission_finishable
			}
		}

		this.missionDirty[missionID] = true
		this.dbDirty = true
		this.receiver.OnMissionDirty()

	}
}

func (this *RoleMission) PackAllMission() []*cs_message.Mission {
	m := make([]*cs_message.Mission, 0)
	for _, v := range this.missions {
		if v.State != mission_end {
			m = append(m, &cs_message.Mission{
				Id:    proto.Int32(v.Id),
				State: v.GetStatePB(),
				Cond:  v.GetCondPB(),
			})
		}
	}
	return m
}

func (this *RoleMission) PackMissionModify() []*cs_message.Mission {
	if len(this.missionDirty) == 0 {
		return nil
	}

	m := make([]*cs_message.Mission, 0)
	for id, b := range this.missionDirty {
		if b {
			delete(this.missionDirty, id)
			v := this.missions[id]
			m = append(m, &cs_message.Mission{
				Id:    proto.Int32(v.Id),
				State: v.GetStatePB(),
				Cond:  v.GetCondPB(),
			})

			/*
			*任务结束 判断是否是循环任务
			*一次性任务将删除
			*目前都删除已完成任务
			 */
			if v.State == mission_end {
				delete(this.missions, v.Id)
			}
		}
	}
	return m
}

func (this *RoleMission) AcceptMission(id int32) bool {
	m := this.missions[id]
	if nil == m {
		node_game.Infoln("AcceptMission nil")
		return false
	}

	if m.State != mission_acceptable {
		node_game.Infoln("AcceptMission State not acceptable")
		return false

	}

	m.State = mission_running
	this.missionDirty[id] = true
	this.dbDirty = true
	this.receiver.OnMissionDirty()
	this.receiver.OnMissionAccept(m)

	return true
}

func (this *RoleMission) CompleteMission(id int32) bool {
	m := this.missions[id]
	if nil == m {
		return false
	}

	if m.State != mission_finishable {
		return false
	}

	m.State = mission_end

	next := m.NextMissionID()

	if next > 0 {
		this.AddNextMission(next)
	}

	this.missionDirty[id] = true
	this.dbDirty = true

	this.receiver.OnMissionEnd(m)
	this.receiver.OnMissionDirty()

	return true
}

func (this *RoleMission) MissionCanAccept(mission *Mission) bool {
	//任务可接取条件判断

	mission.State = mission_acceptable
	if mission.AutoAccept() {
		mission.State = mission_running
		this.receiver.OnMissionAccept(mission)
	} else {
		mission.State = mission_acceptable
	}

	this.missionDirty[mission.Id] = true
	this.dbDirty = true

	return true
}

func (this *RoleMission) AddNextMission(missionID int32) {
	m := newMission(missionID)
	if nil != m {
		this.missions[missionID] = m
		this.MissionCanAccept(m)

		this.missionDirty[missionID] = true
		this.dbDirty = true

	}
}

func (this *RoleMission) PackJson(fields map[string]interface{}) {
	if this.dbDirty {
		jsonStr, err := json.Marshal(this.missions)
		if nil == err {
			fields["mission"] = jsonStr
		}
	}
}

func (this *RoleMission) IsDirty() bool {
	return this.dbDirty
}

func (this *RoleMission) SetDirtyFlag() {
	this.dbDirty = true
}

func (this *RoleMission) ClearDirtyFlag() {
	this.dbDirty = false
}
